/*--------------------------------------------------------------------------------------------------
Yatak Operation System Project Copyright 2012
Author : Ilya Churaev
Started : 4 May 2012		Updated : 4 May 2012
File Name : gdt.c			Purpose : install gdt
--------------------------------------------------------------------------------------------------*/
#include <yatak/debug.h>
#include <yatak/gdt.h>
#include <yatak/idt.h>
#include <screen.h>
#include <types.h>

// Предоставляет нам доступ к нашим ассемблерным функциям из нашего кода на C.
extern void gdt_flush(uint32);
//extern void tss_flush();

// Прототипы внешних функций.
void init_gdt();
void gdt_set_gate(sint32,uint32,uint32,uint8,uint8);
//static void write_tss(sint32 num, uint16 ss0, uint32 esp0);

gdt_entry_t gdt_entries[5];
gdt_ptr_t   gdt_ptr;
//tss_entry_t tss_entry;

void init_gdt()
{
	gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1;
	gdt_ptr.base  = (uint32)&gdt_entries;

	dprintf ("[init_gdt] Install null segment...\n");
	gdt_set_gate(0, 0, 0, 0, 0);                // Null segment
	dprintf ("[init_gdt] Install code segment...\n");
	gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment
	dprintf ("[init_gdt] Install data segment...\n");
	gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment
	dprintf ("[init_gdt] Install user mode code segment...\n");
	gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment
	dprintf ("[init_gdt] Install user mode data segment...\n");
	gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment
	//kprintf ("Install task state segment...\n");
	//write_tss(5, 0x10, 0x0);					// TSS

	gdt_flush((uint32)&gdt_ptr);
	//tss_flush();
	MessageFoS ("Initialising GDT\t\t\t\t\t\t", true);
}

// Set the value of one GDT entry.
void gdt_set_gate(sint32 num, uint32 base, uint32 limit, uint8 access, uint8 gran)
{
    gdt_entries[num].base_low    = (base & 0xFFFF);
    gdt_entries[num].base_middle = (base >> 16) & 0xFF;
    gdt_entries[num].base_high   = (base >> 24) & 0xFF;

    gdt_entries[num].limit_low   = (limit & 0xFFFF);
    gdt_entries[num].granularity = (limit >> 16) & 0x0F;
    
    gdt_entries[num].granularity |= gran & 0xF0;
    gdt_entries[num].access      = access;
}

// Инициализируем нашу структуру сегмента состояния задачи.
/*static void write_tss(sint32 num, uint16 ss0, uint32 esp0)
{
    // Сначала давайте вычислим базу и предельное значение для нашей записи в таблице GDT.
    uint32 base = (uint32) &tss_entry;
    uint32 limit = base + sizeof(tss_entry);
    
    // Теперь добавим в таблицу GDT  адрес дескриптора нашего TSS.
    gdt_set_gate(num, base, limit, 0xE9, 0x00);

    // Обеспечим, чтобы первоначально дескриптор был равен нулю.
    memset(&tss_entry, 0, sizeof(tss_entry));

    tss_entry.ss0  = ss0;	// Запоминаем сегмент стека ядра.
    tss_entry.esp0 = esp0;	// Запоминаем указатель стека ядра.
    
    // Здесь заносим в таблицу TSS записи cs, ss, ds, es, fs и gs. В них указывается, какие сегменты
	// должны быть загружены в случае,  когда процессор переключается в режим ядра. Поэтому
	// они являются нашими обычными сегментами кода/данных ядра - 0x08 и 0x10 соответственно,
	// но в последних двух битах будут указаны значения 0x0b и 0x13. Значения этих битов указывают,
	// что уровень запрашиваемых привилегий RPL (requested privilege level) равен 3; это означает, что 
	// этот сегмент TSS  можно использовать для переключения в режим ядра из кольца 3.
    tss_entry.cs   = 0x0b;     
    tss_entry.ss = tss_entry.ds = tss_entry.es = tss_entry.fs = tss_entry.gs = 0x13;
}*/

/*void set_kernel_stack(uint32 stack)
{
    tss_entry.esp0 = stack;
}*/
